from django import http
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
import re, json, logging
from django.db import DatabaseError
from utils.response_code import RETCODE
from django_redis import get_redis_connection
from django.conf import settings

from users.models import User, Addresses
from utils.response_code import  RETCODE
from utils.views import  LoginRequiredJSONMixin
from celery_tasks.email.tasks import send_verify_email
from .  import contants
from .utils import generate_email_verify_url, check_email_verify_url
from goods.models import SKU
from . import contants
from carts.utils import merge_carts_cookie_redis


logger = logging.getLogger('django')


# Create your views here.
class UserBrowseRecordView(LoginRequiredJSONMixin, View):
    def post(self, request):
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        sku_id = json_dict.get('sku_id')

        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('Invalid SKU id')

        user = request.user
        redis_conn = get_redis_connection('history')
        rl = redis_conn.pipeline()
        rl.lrem('history_%s' % user.id, contants.REDIS_PARAMETER_ZERO, sku_id)
        rl.lpush('history_%s' % user.id, sku_id)
        rl.ltrim('history_%s' % user.id,  contants.REDIS_PARAMETER_ZERO, contants.REDIS_USER_HISTORY_RECORDS)

        rl.execute()

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})


    def get(self, request):
        user = request.user
        redis_conn = get_redis_connection('history')
        sku_ids = redis_conn.lrange('history_%s' % user.id, 0, -1)

        skus = []
        for sku_id in sku_ids:
            try:
                sku = SKU.objects.get(id=sku_id)
            except SKU.DoesNotExist:
                return http.HttpResponseForbidden('Browse record does not exist')

            skus.append({
                'id': sku.id,
                'name': sku.name,
                'price': sku.price,
                'default_image_url': sku.default_image.url
            })

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': skus})



class ChangePasswordView(LoginRequiredMixin, View):
    def get(self, request):
        # user = User.objects.get(username='mgz080')
        # user.set_password('12345678')
        # user.save()
        return render(request, 'user_center_pass.html')

    def post(self, request):
        old_passwd = request.POST.get('old_password')
        new_passwd = request.POST.get('new_password')
        new_passwd2 = request.POST.get('new_password2')

        if not all([old_passwd, new_passwd, new_passwd2]):
            return http.HttpResponseForbidden('Missing required parameters')

        if not re.match(r'^[0-9a-zA-Z]{8,20}$', old_passwd):
            return http.HttpResponseForbidden('Password format error')

        if not re.match(r'^[0-9a-zA-Z]{8,20}$', new_passwd):
            return http.HttpResponseForbidden('Password format error')

        if not re.match(r'^[0-9a-zA-Z]{8,20}$', new_passwd2):
            return http.HttpResponseForbidden('Password format error')

        if new_passwd != new_passwd2:
            return http.HttpResponseForbidden('Password format error')

        login_user = request.user
        if not login_user.check_password(old_passwd):
            return render(request, 'user_center_pass.html', {'origin_password_errmsg': 'Incorrect password'})

        try:
            login_user.set_password(new_passwd)
            login_user.save()
        except Exception as e:
            logger(e)
            return render(request, 'user_center_pass.html', {'origin_password_errmsg': 'Incorrect password'})

        next = '?next=/info/'
        logout(request)
        response = redirect(reverse('users:login') + next)
        response.delete_cookie('username')

        return response



class DefaultAddressView(LoginRequiredJSONMixin, View):
    def put(self, request, address_id):
        try:
            address = Addresses.objects.get(id=address_id)
            request.user.default_address = address
            request.user.save()
        except Exception as e:
            logger(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'Update default address failed'})

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Update default address successful'})


class UpdateDestroyAddressView(LoginRequiredJSONMixin, View):
    def put(self, request, address_id):
        address_dict = json.loads(request.body.decode())

        title = address_dict.get('receiver')
        receiver = title
        province_id = address_dict.get('province_id')
        city_id = address_dict.get('city_id')
        district_id = address_dict.get('district_id')
        place = address_dict.get('place')
        mobile = address_dict.get('mobile')
        tel = address_dict.get('tel')
        email = address_dict.get('email')

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('Missing required parameters')

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('Mobile format error')

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('Tel format error')

        if email:
            if not re.match(r'^[0-9a-z][\w\.\-]*@[0-9a-z-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('Email format error')

        try:
            Addresses.objects.filter(id=address_id).update(
                user=request.user,
                title=title,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'Create address failed'})

        try:
            address = Addresses.objects.get(id=address_id)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'Query address failed'})

        address_dict2 = {
            'id': address.id,
            'title': address.title,
            'receiver': address.receiver,
            'province': address.province.name,
            'city': address.city.name,
            'district': address.district.name,
            'place': address.place,
            'mobile': address.mobile,
            'email': address.email,
            'tel': address.tel
        }

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Create address successful', 'address': address_dict2})


    def delete(self, request, address_id):
        try:
            address = Addresses.objects.get(id=address_id)
            address.is_deleted = True
            address.save()

            # users_list = address.users.all()
            # for user in users_list:
            #     user.default_address_id = ''
            #     user.save()

            if request.user.default_address_id == int(address_id):
                request.user.default_address_id = ''
                request.user.save()

        except Exception as e:
            logger(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'Delete address failed'})

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Delete address successful'})


class UpdateAddressTitleView(LoginRequiredJSONMixin, View):
    def put(self, request, address_id):
        title_dict = json.loads(request.body.decode())
        title = title_dict.get('title')

        if not title:
            return http.HttpResponseForbidden('Missing required parameters')

        try:
            address = Addresses.objects.get(id=address_id)
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'Update Title failed'})

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Update Title Successful'})


class AddressCreateView(LoginRequiredJSONMixin, View):
    def post(self, request):
        if request.user.addresses.count() >= contants.USER_ADDRESS_COUNTS_LIMIT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': 'Upper limit exceeded'})

        address_dict = json.loads(request.body.decode())

        title = address_dict.get('receiver')
        receiver = title
        province_id = address_dict.get('province_id')
        city_id = address_dict.get('city_id')
        district_id = address_dict.get('district_id')
        place = address_dict.get('place')
        mobile = address_dict.get('mobile')
        tel = address_dict.get('tel')
        email = address_dict.get('email')

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('Missing required parameters')

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('Mobile format error')

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('Tel format error')

        if email:
            if not re.match(r'^[0-9a-z][\w\.\-]*@[0-9a-z-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('Email format error')

        try:
            address = Addresses.objects.create(
                user = request.user,
                title = title,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email
            )

            if not  request.user.default_address:
                request.user.default_address = address
                request.user.save()

        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'Create address failed'})

        address_dict2 = {
            'id': address.id,
            'title': address.title,
            'receiver': address.receiver,
            'province': address.province.name,
            'city': address.city.name,
            'district': address.district.name,
            'place': address.place,
            'mobile': address.mobile,
            'email': address.email,
            'tel': address.tel
        }

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Create address successful', 'address': address_dict2})


class AddressesView(LoginRequiredMixin, View):
    def get(self, request):
        login_user = request.user
        addresses = Addresses.objects.filter(user=login_user, is_deleted=False)

        address_list = []
        for address in addresses:
            address_dict = {
                'id': address.id,
                'title': address.title,
                'receiver': address.receiver,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'email': address.email,
                'tel': address.tel
            }
            address_list.append(address_dict)

        if not login_user.default_address_id:
            default_address_id = contants.USER_DEFAULT_ADDRESS_NOT_EXIST
        else:
            default_address_id = login_user.default_address_id

        context = {
            'default_address_id': default_address_id,
            'addresses': address_list
        }

        return render(request, 'user_center_site.html', context)



class ActivateEmailView(View):
    def get(self, request):
        mail_token = request.GET.get('token')
        if not mail_token:
            return http.HttpResponseForbidden('Token error')

        user = check_email_verify_url(mail_token)
        if not user:
            return http.HttpResponseForbidden('Token error')

        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return http.HttpResponseForbidden('Activate email failure')

        return redirect(reverse('users:info'))


class EmailView(LoginRequiredJSONMixin, View):
    def put(self, request):
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        mail = json_dict.get('email')

        if not re.match(r'^[0-9a-z][\w\.\-]*@[a-z0-9-]+(\.[a-z]{3,5}){1,2}$', mail):
            return http.HttpResponseForbidden('Incorrect mailbox format')

        try:
            request.user.email = mail
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': 'Failed to add mailbox'})

        mail_url = generate_email_verify_url(request.user)
        send_verify_email.delay(mail, mail_url)

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})


class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }
        return render(request, 'user_center_info.html', context)


class UserLogoutView(View):
    def get(self, request):
        logout(request)
        response = redirect(reverse('contents:index'))
        response.delete_cookie('username')

        return response


class UserLoginView(View):
    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')

        if not all([username, password]):
            return http.HttpResponseForbidden('Missing required parameters')

        if not re.match(r'^[0-9a-zA-Z_-]{5,20}$', username):
            return http.HttpResponseForbidden('Incorrect user name')

        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password):
            return http.HttpResponseForbidden('Incorrect password')

        user = authenticate(username=username, password=password)
        if user is None:
            return render(request, 'login.html', {'account_errmsg': 'Incorrect username or password'})

        login(request, user)

        if remembered != 'on':
            request.session.set_expiry(0)
        else:
            request.session.set_expiry(3600)

        next = request.GET.get('next')
        if next:
            response = redirect(next)
        else:
            response = redirect(reverse('contents:index'))

        response.set_cookie('username', user.username, max_age=3600)

        response = merge_carts_cookie_redis(request=request, response=response, user=user)

        return response



class RegisterView(View):
    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):
        req_obj = request.POST
        username = req_obj.get('username')
        password = req_obj.get('password')
        password2 = req_obj.get('password2')
        mobile = req_obj.get('mobile')
        sms_code_client = req_obj.get('sms_code')
        allow = req_obj.get('allow')

        if not all([username, password, password2, mobile, allow]):
            return http.HttpResponseForbidden('Missing required parameters')

        if not re.match(r'^[0-9a-zA-Z_-]{5,20}$', username):
            return http.HttpResponseForbidden('User name needs 5 to 20 characters')

        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password):
            return http.HttpResponseForbidden('Password needs 8 to 20 characters')

        if password != password2:
            return http.HttpResponseForbidden('Inconsistent passwords')

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('Incorrect phone number format')

        redis_conn = get_redis_connection('verify_code')
        sms_code_server = redis_conn.get('sms_%s' % mobile)
        if sms_code_server is None:
            return render(request, 'register.html', {'sms_code_error': 'SMS code has expired'})
        if sms_code_client != sms_code_server.decode():
            return render(request, 'register.html', {'sms_code_error': 'Incorrect SMS code'})

        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except DatabaseError:
            return render(request, 'register.html', {'register_errmsg': 'Register Failure'})

        login(request, user)

        response = redirect(reverse('contents:index'))
        response.set_cookie('username', user.username, max_age=3600)

        return response


class CheckUserNameView(View):
    def get(self, request, username):
        count = User.objects.filter(username=username).count()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Ok', 'count': count})


class CheckMobileView(View):
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'Ok', 'count': count})
